package cn.song.freight.service.impl;

import cn.song.freight.domain.dto.BatchDeleteStrategyAssignmentDTO;
import cn.song.freight.domain.dto.BatchStrategyAssignmentDTO;
import cn.song.freight.domain.dto.StrategyAssignmentDTO;
import cn.song.freight.domain.query.StrategyAssignmentQuery;
import cn.song.freight.domain.entity.StrategyAssignmentDO;
import cn.song.freight.domain.result.R;
import cn.song.freight.mapper.StrategyAssignmentMapper;
import cn.song.freight.service.StrategyAssignmentService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 运费策略分配服务实现类
 * <pre>
 * 该服务实现提供物流系统的运费策略分配管理功能，支持单个和批量分配关系的增删改查操作，
 * 并提供基于多种条件的分配关系查询功能，用于维护系统中仓库、物流公司与运费策略的关联配置。
 *
 * 核心功能：
 * - 运费策略分配关系的添加、删除、更新和查询
 * - 支持单个和批量分配关系操作
 * - 数据完整性校验和业务规则验证
 * - 分配关系的状态管理和使用情况检查
 *
 * 分配关系说明：
 * 一个仓库与一个物流公司的组合可以分配一个运费策略，用于计算该组合下的运费
 *
 * @author SongRenShuo
 * @version 3.0.0
 * @since 2025-08-23
 */
@Slf4j
@Service
public class StrategyAssignmentServiceImpl extends ServiceImpl<StrategyAssignmentMapper, StrategyAssignmentDO> implements StrategyAssignmentService {

    /**
     * 分配运费策略
     * <pre>
     * 该方法实现运费策略的分配功能，会先校验传入的仓库编码和物流公司编码组合是否已存在，
     * 避免重复分配，确保数据的唯一性和一致性，并记录操作时间和操作人信息。
     *
     * @param strategyAssignmentDTO 运费策略分配数据传输对象，包含仓库编码、物流公司编码和策略ID等信息
     * @return 分配结果，true表示分配成功，false表示分配失败
     */
    @Override
    public R<Boolean> assignStrategy(StrategyAssignmentDTO strategyAssignmentDTO) {
        // 校验传入的仓库编码和物流公司编码组合是否已存在
        LambdaQueryWrapper<StrategyAssignmentDO> wrapper = Wrappers.<StrategyAssignmentDO>lambdaQuery()
                .eq(StrategyAssignmentDO::getWarehouseCode, strategyAssignmentDTO.getWarehouseCode())
                .eq(StrategyAssignmentDO::getLogisticsCompanyCode, strategyAssignmentDTO.getLogisticsCompanyCode())
                // 更新操作时排除当前记录
                .ne(strategyAssignmentDTO.getId() != null, StrategyAssignmentDO::getId, strategyAssignmentDTO.getId());

        if (this.count(wrapper) > 0) {
            return R.failed("该仓库与物流公司的组合已存在运费策略分配，请勿重复添加");
        }
        strategyAssignmentDTO.setStatus(strategyAssignmentDTO.getStatus() == null ? 1 : strategyAssignmentDTO.getStatus());

        // 构建 DO 对象并填充属性
        StrategyAssignmentDO entity = new StrategyAssignmentDO();
        BeanUtils.copyProperties(strategyAssignmentDTO, entity);
        entity.setCreateTime(new Date());
        entity.setUpdateTime(new Date());

        // 插入数据库
        boolean result = this.save(entity);
        return R.isSuccess(result);
    }

    /**
     * 批量分配运费策略
     * <pre>
     * 该方法实现运费策略的批量分配功能，会对传入的仓库编码和物流公司编码进行笛卡尔积组合，
     * 逐一校验组合是否已存在，避免重复分配，并记录操作时间和操作人信息，提高分配效率。
     *
     * @param batchDTO 批量运费策略分配数据传输对象，包含仓库编码列表、物流公司编码列表和策略ID等信息
     * @return 批量分配结果，包含成功分配的记录数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public R<Integer> batchAssignStrategy(BatchStrategyAssignmentDTO batchDTO) {
        int successCount = 0;
        List<String> skippedCombinations = new ArrayList<>();
        Date now = new Date();

        // 逐个遍历仓库编码和物流公司编码的组合
        for (String warehouseCode : batchDTO.getWarehouseCodes()) {
            for (String logisticsCode : batchDTO.getLogisticsCompanyCodes()) {
                // 检查组合是否已存在
                LambdaQueryWrapper<StrategyAssignmentDO> wrapper = Wrappers.<StrategyAssignmentDO>lambdaQuery()
                        .eq(StrategyAssignmentDO::getWarehouseCode, warehouseCode)
                        .eq(StrategyAssignmentDO::getLogisticsCompanyCode, logisticsCode);

                if (this.count(wrapper) > 0) {
                    skippedCombinations.add(warehouseCode + "-" + logisticsCode);
                    continue;
                }

                // 创建新的分配记录
                StrategyAssignmentDO entity = new StrategyAssignmentDO();
                entity.setWarehouseCode(warehouseCode);
                entity.setLogisticsCompanyCode(logisticsCode);
                entity.setStrategyId(batchDTO.getStrategyId());
                entity.setStatus(batchDTO.getStatus());
                entity.setOperator(batchDTO.getOperator());
                entity.setCreateTime(now);
                entity.setUpdateTime(now);

                if (this.save(entity)) {
                    successCount++;
                }
            }
        }

        String message = String.format("批量分配完成，成功创建%d条记录", successCount);
        if (!skippedCombinations.isEmpty()) {
            message += String.format("，跳过已存在的组合%d个", skippedCombinations.size());
        }

        return R.success(message, successCount);
    }

    /**
     * 删除运费策略分配
     * <pre>
     * 该方法实现运费策略分配的删除功能，会先校验分配关系是否存在，
     * 确保删除操作的安全性和准确性，避免误删不存在的数据，并记录操作日志便于追踪。
     *
     * @param strategyAssignmentId 要删除的运费策略分配ID
     * @return 删除结果，true表示删除成功，false表示删除失败
     */
    @Override
    public R<Boolean> deleteStrategyAssignment(Long strategyAssignmentId) {
        if (strategyAssignmentId == null) {
            return R.failed("策略分配ID不能为空");
        }

        StrategyAssignmentDO entity = this.getById(strategyAssignmentId);
        if (entity == null) {
            return R.failed("策略分配不存在");
        }

        boolean result = this.removeById(strategyAssignmentId);
        return R.isSuccess(result);

    }

    /**
     * 批量删除运费策略分配
     * <pre>
     * 该方法实现运费策略分配的批量删除功能，会对传入的ID列表进行校验，
     * 确保要删除的记录存在，并记录操作日志便于追踪，提高删除效率并保证数据一致性。
     *
     * @param batchDeleteDTO 批量删除运费策略分配数据传输对象，包含要删除的ID列表
     * @return 批量删除结果，包含成功删除的记录数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public R<Integer> batchDeleteStrategy(BatchDeleteStrategyAssignmentDTO batchDeleteDTO) {
        // 参数验证
        if (batchDeleteDTO == null) {
            return R.failed("请求参数不能为空");
        }

        if (batchDeleteDTO.getIds() == null || batchDeleteDTO.getIds().isEmpty()) {
            return R.failed("删除ID列表不能为空");
        }

        log.info("开始批量删除策略分配，ID列表: {}", batchDeleteDTO.getIds());

        // 检查要删除的记录是否存在
        List<StrategyAssignmentDO> existingRecords = this.listByIds(batchDeleteDTO.getIds());
        if (existingRecords.isEmpty()) {
            return R.failed("没有找到要删除的记录");
        }

        if (existingRecords.size() != batchDeleteDTO.getIds().size()) {
            log.warn("部分记录不存在，请求删除{}条，实际找到{}条", batchDeleteDTO.getIds().size(), existingRecords.size());
        }

        // 执行批量删除
        boolean result = this.removeByIds(batchDeleteDTO.getIds());
        if (!result) {
            log.error("批量删除执行失败");
            return R.failed("批量删除执行失败");
        }

        int deletedCount = existingRecords.size();
        log.info("批量删除成功，删除了{}条记录", deletedCount);
        return R.success(String.format("批量删除成功，共删除%d条记录", deletedCount), deletedCount);
    }

    /**
     * 查询运费策略分配列表
     * <pre>
     * 该方法实现运费策略分配的条件查询功能，支持按仓库编码、物流公司编码、策略ID和状态进行查询，
     * 查询结果包含符合条件的所有分配关系数据，便于查看和管理分配配置信息。
     *
     * @param query 运费策略分配查询对象，包含查询条件
     * @return 符合条件的运费策略分配列表
     */
    @Override
    public R<List<StrategyAssignmentDO>> getStrategyAssignments(StrategyAssignmentQuery query) {
        LambdaQueryWrapper<StrategyAssignmentDO> wrapper = Wrappers.<StrategyAssignmentDO>lambdaQuery()
                // 按仓库编码模糊查询
                .like(StrUtil.isNotBlank(query.getWarehouseCode()), StrategyAssignmentDO::getWarehouseCode, query.getWarehouseCode())
                // 按物流公司编码模糊查询
                .like(StrUtil.isNotBlank(query.getLogisticsCompanyCode()), StrategyAssignmentDO::getLogisticsCompanyCode, query.getLogisticsCompanyCode())
                // 按策略ID精确匹配
                .eq(query.getStrategyId() != null, StrategyAssignmentDO::getStrategyId, query.getStrategyId())
                // 按状态精确匹配
                .eq(query.getStatus() != null, StrategyAssignmentDO::getStatus, query.getStatus())
                // 按创建时间倒序排列
                .orderByDesc(StrategyAssignmentDO::getCreateTime);
        List<StrategyAssignmentDO> result = this.list(wrapper);
        return R.success("查询成功", result);
    }

    /**
     * 更新运费策略分配状态
     * <pre>
     * 该方法实现运费策略分配状态的更新功能，会先校验分配关系是否存在，
     * 然后根据传入的状态值对分配关系进行状态更新操作，确保数据的实时性和准确性，并记录更新时间。
     *
     * @param strategyAssignmentId 要更新状态的运费策略分配ID
     * @param status 新的状态值
     * @return 更新结果，true表示更新成功，false表示更新失败
     */
    @Override
    public R<Boolean> updateStrategyAssignmentStatus(Long strategyAssignmentId, Integer status) {
        StrategyAssignmentDO entity = this.getById(strategyAssignmentId);
        if (entity == null) {
            return R.failed("运费策略分配不存在");
        }

        entity.setStatus(status);
        entity.setUpdateTime(new Date());

        boolean result = this.updateById(entity);
        return R.isSuccess(result);
    }
}
